package com.inspur.edp.common.type;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.inspur.edp.common.type.exception.TypeException;
import com.inspur.edp.common.type.parser.TypeCompareMgr;
import com.inspur.edp.common.type.utils.TypeUtil;
import io.iec.edp.caf.commons.utils.StringUtils;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author lizhaorui
 * @date 2025/8/14
 * @description
 */

@Data
public class TypeRefer {

    private String source;

    private String typeId;

    private String typeCode;

    private String typeName;


    private List<TypeRefer> genericTypes;

    public TypeRefer() {

    }

    public TypeRefer(String typeId) {
        this.source = "default";
        this.typeId = typeId;
    }

    public TypeRefer(String source, String typeId) {
        this.source = source;
        this.typeId = typeId;
    }

    //1、字符串类型
    public static final String STRING = "string";
    public static final TypeRefer STRING_TYPE = new TypeRefer(STRING);

    //2、数字类型
    public static final String NUMBER = "number";
    public static final String INT = "int";
    public static final String DOUBLE = "double";
    public static final String FLOAT = "float";
    public static final String LONG = "long";
    public static final String BIGDECIMAL = "java.math.BigDecimal";
    public static final String COLLECTION = "java.util.Collection";
    public static final String ITERABLE = "java.lang.Iterable";
    public static final String ARRAY_NODE = "com.fasterxml.jackson.databind.node.ArrayNode";

    public static final TypeRefer NUMBER_TYPE = new TypeRefer(NUMBER);
    public static final TypeRefer INT_TYPE = new TypeRefer(INT);
    public static final TypeRefer DOUBLE_TYPE = new TypeRefer(DOUBLE);
    public static final TypeRefer FLOAT_TYPE = new TypeRefer(FLOAT);
    public static final TypeRefer LONG_TYPE = new TypeRefer(LONG);
    public static final TypeRefer BIGDECIMAL_TYPE = new TypeRefer(BIGDECIMAL);
    public static final TypeRefer COLLECTION_TYPE = new TypeRefer(COLLECTION);
    public static final TypeRefer ITERABLE_TYPE = new TypeRefer(ITERABLE);
    public static final TypeRefer ARRAY_NODE_TYPE = new TypeRefer(ARRAY_NODE);

    //3、时间类型
    public static final String DATETIME = "java.util.Date";

    public static final String OFFSET_DATETIME = "java.time.OffsetDateTime";

    public static final TypeRefer DATETIME_TYPE = new TypeRefer(DATETIME);
    public static final TypeRefer OFFSET_DATETIME_TYPE = new TypeRefer(OFFSET_DATETIME);

    //4、布尔类型
    public static final String BOOLEAN = "boolean";
    public static final TypeRefer BOOLEAN_TYPE = new TypeRefer(BOOLEAN);

    //5、任意类型
    public static final String ANY = "any";
    public static final TypeRefer ANY_TYPE = new TypeRefer(ANY);

    //6、空类型
    public static final String VOID = "void";
    public static final TypeRefer VOID_TYPE = new TypeRefer(VOID);

    //7、数组类型
    public static final String ARRAY = "array";

    public static final String TEMP_SOURCE = "temporary";

    public static final String JSON_SCHEMA_SOURCE = "jsonSchema";

    //8、列表类型
    public static final String LIST = "list";
    public static final TypeRefer LIST_STRING_TYPE;
    public static final TypeRefer LIST_INT_TYPE;
    public static final TypeRefer LIST_DOUBLE_TYPE;
    public static final TypeRefer LIST_BOOLEAN_TYPE;
    public static final TypeRefer LIST_ANY_TYPE;

    static {

        LIST_STRING_TYPE = new TypeRefer(LIST);
        LIST_STRING_TYPE.addGenericType(STRING_TYPE);

        LIST_INT_TYPE = new TypeRefer(LIST);
        LIST_INT_TYPE.addGenericType(INT_TYPE);

        LIST_DOUBLE_TYPE = new TypeRefer(LIST);
        LIST_DOUBLE_TYPE.addGenericType(DOUBLE_TYPE);

        LIST_BOOLEAN_TYPE = new TypeRefer(LIST);
        LIST_BOOLEAN_TYPE.addGenericType(BOOLEAN_TYPE);

        LIST_ANY_TYPE = new TypeRefer(LIST);
        LIST_ANY_TYPE.addGenericType(ANY_TYPE);

    }

    @JsonIgnore
    public void addGenericType(TypeRefer genericType) {
        if (this.genericTypes == null) {
            this.genericTypes = new ArrayList<>();
        }
        this.genericTypes.add(genericType);
    }

    @JsonIgnore
    public boolean isString() {
        return this.typeId.equalsIgnoreCase("string");
    }

    @JsonIgnore
    public boolean isNumber() {
        return this.typeId.equalsIgnoreCase(NUMBER) ||
                this.typeId.equalsIgnoreCase(INT) ||
                this.typeId.equalsIgnoreCase(DOUBLE) ||
                this.typeId.equalsIgnoreCase(BIGDECIMAL) ||
                this.typeId.equalsIgnoreCase(FLOAT) ||
                this.typeId.equalsIgnoreCase(LONG);
    }


    @JsonIgnore
    public boolean isAny() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        return this.typeId.equalsIgnoreCase(ANY);
    }

    @JsonIgnore
    public boolean isInt() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        return this.typeId.equalsIgnoreCase(INT_TYPE.typeId);
    }

    @JsonIgnore
    public boolean isDouble() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        return this.typeId.equalsIgnoreCase(DOUBLE_TYPE.typeId);
    }

    @JsonIgnore
    public boolean isFloat() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        return this.typeId.equalsIgnoreCase(FLOAT);
    }

    @JsonIgnore
    public boolean isLong() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        return this.typeId.equalsIgnoreCase(LONG);
    }

    @JsonIgnore
    public boolean isBigDecimal() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        return this.typeId.equalsIgnoreCase(BIGDECIMAL_TYPE.typeId);
    }


    @JsonIgnore
    public boolean isBoolean() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        return this.typeId.equalsIgnoreCase(BOOLEAN);
    }

    @JsonIgnore
    public boolean isDateTime() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        return this.typeId.equalsIgnoreCase(DATETIME);
    }

    @JsonIgnore
    public boolean isOffsetDateTime() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        return this.typeId.equalsIgnoreCase(OFFSET_DATETIME);
    }

    @JsonIgnore
    public boolean isList() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        return this.typeId.equalsIgnoreCase(LIST);
    }

    @JsonIgnore
    public boolean isIterable() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        if (this.typeId.equalsIgnoreCase(ITERABLE)) {
            return true;
        } else {
            boolean isInst = TypeCompareMgr.isInstance(this, ITERABLE_TYPE);
            if (isInst) {
                return true;
            } else {
                return false;
            }
        }
    }

    @JsonIgnore
    public boolean isArray() {
        if (StringUtils.isEmpty(this.typeId)) {
            return false;
        }
        return this.typeId.equalsIgnoreCase(ARRAY);
    }


    @JsonIgnore
    public TypeRefer tryReplaceGenericType(Map<String, TypeRefer> varTypeMap) {
        boolean needReplace = isContainVarType(this, varTypeMap);
        if (needReplace) {
            return replaceGenericType(varTypeMap);
        } else {
            return this;
        }
    }

    @JsonIgnore
    public TypeRefer replaceGenericType(Map<String, TypeRefer> varTypeMap) {
        TypeRefer newTypeRefer = this.createNewInstance();
        replaceGenericType(this, newTypeRefer, varTypeMap);
        return newTypeRefer;
    }

    @JsonIgnore
    private boolean isContainVarType(TypeRefer refer, Map<String, TypeRefer> varTypeMap) {
        if (varTypeMap == null) {
            return false;
        }
        if (varTypeMap.containsKey(refer.typeId)) {
            return true;
        }
        if (refer.getGenericTypes() == null || refer.getGenericTypes().size() == 0) {
            return false;
        }
        for (TypeRefer childRefer : refer.getGenericTypes()) {
            boolean childResult = isContainVarType(childRefer, varTypeMap);
            if (childResult) {
                return true;
            }
        }
        return false;
    }

    @JsonIgnore
    private void replaceGenericType(TypeRefer refer, TypeRefer newRefer, Map<String, TypeRefer> varTypeMap) {
        if (varTypeMap.containsKey(refer.typeId)) {
            newRefer.typeId = varTypeMap.get(refer.typeId).getTypeId();
        } else {
            newRefer.typeId = refer.getTypeId();
        }
        if (refer.getGenericTypes() == null || refer.getGenericTypes().size() == 0) {
            return;
        }
        for (TypeRefer childRefer : refer.getGenericTypes()) {
            TypeRefer newChildRefer = childRefer.createNewInstance();
            replaceGenericType(childRefer, newChildRefer, varTypeMap);
        }
    }

    @JsonIgnore
    public TypeRefer createNewInstance() {
        return new TypeRefer();
    }


    @JsonIgnore
    public TypeRefer getGenericType(int index) {
        if (this.getGenericTypes() == null || this.getGenericTypes().size() <= index) {
            throw new TypeException(String.format("%s generic type count less than  %s .", this.toString(), index + 1));
        }
        return this.getGenericTypes().get(index);
    }


    @JsonIgnore
    public String getText() {
        StringBuilder sb = new StringBuilder();
        String url = TypeUtil.getTypeUrl(this);
        String typeName = TypeUtil.getTypeCode(url);
        sb.append(typeName);
        if (this.genericTypes != null && this.genericTypes.size() > 0) {
            sb.append("<");
            int index = 0;
            for (TypeRefer childRefer : this.genericTypes) {
                if (index > 0) {
                    sb.append(",");
                }
                String text = childRefer.getText();
                sb.append(text);
                index++;
            }
            sb.append(">");
        }
        return sb.toString();
    }

    @JsonIgnore
    public List<String> getImports() {
        List<String> imports = new ArrayList<>();
        recursionGetImports(this, imports);
        return imports;
    }

    @JsonIgnore
    private void recursionGetImports(TypeRefer typeRefer, List<String> imports) {
        String typeUrl = TypeUtil.getTypeUrl(this);
        imports.add(typeUrl);
        if (typeRefer.genericTypes != null && typeRefer.genericTypes.size() > 0) {
            for (TypeRefer childRefer : typeRefer.genericTypes) {
                recursionGetImports(childRefer, imports);
            }
        }
    }


    @JsonIgnore
    private void recursionGetImportTypes(TypeRefer typeRefer, List<TypeRefer> imports) {
        imports.add(typeRefer);
        if (typeRefer.genericTypes != null && typeRefer.genericTypes.size() > 0) {
            for (TypeRefer childRefer : typeRefer.genericTypes) {
                recursionGetImportTypes(childRefer, imports);
            }
        }
    }

    @JsonIgnore
    public List<TypeRefer> getImportTypes() {
        List<TypeRefer> imports = new ArrayList<>();
        recursionGetImportTypes(this, imports);
        return imports;
    }

}